/*
 *  Kara IM - Kara Instant Messenger
 *  Copyright (C) 2007-2008 Markus Karnik
 *
 *  This program is free software; you can redistribute it and/or modify it 
 *  under the terms of the GNU General Public License as published by the
 *  Free Software Foundation; either version 3 of the License, 
 *  or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 *  for more details.
 *
 *  You should have received a copy of the GNU General Public License along 
 *  with this program; if not, see <http://www.gnu.org/licenses/>.
 *  
 */

package de.karnik.karaim;

import android.app.Dialog;
import android.app.ListActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
import de.karnik.helper.LogFunctions;
import de.karnik.libkara.pluginspecs.BaseProtocol;
import de.karnik.libkara.roster.ImContact;
import de.karnik.libkara.roster.ImRoster;
import de.karnik.libkara.roster.ImRosterListener;

/**
 * This class implements the main contact list activity.
 * 
 * @author Markus Karnik - markus.karnik@my-design.org
 * @version 1.0
 * @since 1.0
 */
public class KaraIM extends ListActivity implements MenuListener, ServiceConnection, OnItemClickListener {
	
	/**
	 * The Menu of Kara IM.
	 */
	private KaraMenu karaMenu = null;
	/**
	 * The Handler object for the Kara IM activity.
	 */
	public final Handler listHandler = new Handler();
	/**
	 * The current main status.
	 */
	public static int status = -500;
	/**
	 * The reference to the KaraIMService object.
	 */
	private KaraIMServiceAIDL karaIMService = null;
	/**
	 * The intent for the KaraIMService object.
	 */
	private Intent karaImServIntent = null;
	/**
	 * Indicates system shutdown.
	 */
	private boolean shutdown = false;
    /**
     * Runnable to post contact list updates.
     */
    final Runnable updateContactList = new Runnable() {
        public void run() {
        	updateContactList();
        }
    };
    
    /**
     * Runnable to post status changes.
     */
    final Runnable changeStatus = new Runnable() {
        public void run() {
        	setStatus();
        }
    };
    
    /**
     * Runnable to Kara IM.
     */
    final Runnable stopKaraIM = new Runnable() {
        public void run() {
        	stopSystem();
        }
    };

    /**
     * Stop the complete system.
     */
    private void stopSystem() {
    	
    	try {
    		if( karaIMService != null )
    			karaIMService.stopSystem();
		} catch (DeadObjectException e) {
			LogFunctions.log( "KaraIm.setStatus", "Exception", e.getMessage() );
			LogFunctions.log( "KaraIm.setStatus", "Exception", e.getStackTrace() );
		}
    }
    
    /**
     * Informs the service to change the status.
     */
    private void setStatus() {
    	
    	try {
    		if( karaIMService != null ) {
    			karaIMService.changeStatus( status );
    			statusChanged();
    			
    			// TODO SEND ERROR ON FAILURE
    		}
		} catch (DeadObjectException e) {
			LogFunctions.log( "KaraIm.setStatus", "Exception", e.getMessage() );
			LogFunctions.log( "KaraIm.setStatus", "Exception", e.getStackTrace() );
		}
    }
	
    /**
     * Updates the contact list (roster).
     */
    private void updateContactList() {
    	RosterAdapter adapter = ( RosterAdapter )getListAdapter();
    	adapter.notifyDataSetChanged();
    	this.getListView().invalidate();
    }
    
	/**
	 * Sets the status text in the lower left corner.
	 */
	public void statusChanged() {
		TextView view = ( TextView )findViewById( R.id.statustext );
		
		switch( status ) {
			case BaseProtocol.STATUS_AVAILABLE:
				view.setText( getText(R.string.menu_available) );
				break;
			case BaseProtocol.STATUS_AWAY:
				view.setText( R.string.menu_away );
				break;
			case BaseProtocol.STATUS_DND:
				view.setText( R.string.menu_dnd );
				break;
			case BaseProtocol.STATUS_INVISIBLE:
				view.setText( R.string.menu_invisible );
				break;
			case BaseProtocol.STATUS_OFFLINE:
				view.setText( R.string.menu_offline );
				break;
			default:
				view.setText( R.string.menu_offline );
				break;
		}
	}
    
    /* (non-Javadoc)
     * @see android.app.ListActivity#onCreate(android.os.Bundle)
     */
    @Override
    protected void onCreate( Bundle icicle ) {
        super.onCreate( icicle );
        
        LogFunctions.log( "KaraIM.onCreate", "set layout" );
        setContentView( R.layout.kara_im );
        getListView().setOnItemClickListener( this );
        
        LogFunctions.log( "KaraIM.onCreate", "set list adapter" );
        // Use our own list adapter
        setListAdapter( new RosterAdapter( this ) );
        
        LogFunctions.log( "KaraIM.onCreate", "establish connection" );
        // Establish a connection with the service, by its class name.        
        karaImServIntent = new Intent( KaraIM.this,  KaraIMService.class );
        startService( karaImServIntent, new Bundle() );
      
    }
    
    /* (non-Javadoc)
     * @see android.app.Activity#onResume()
     */
    @Override
    protected void onResume() {
    	super.onResume();
    	// bind the service
    	bindService( karaImServIntent, this, 0 );
    	statusChanged();
    	updateContactList();
    }
    
    /* (non-Javadoc)
     * @see android.app.Activity#onPause()
     */
    @Override
    protected void onPause() {
    	super.onPause();
    	// unbind the service
    	this.unbindService( this );
    }
      
    /* (non-Javadoc)
     * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
     */
    @Override
    public boolean onCreateOptionsMenu( Menu menu ) {
        super.onCreateOptionsMenu( menu );
        // build the Menu
        karaMenu = new KaraMenu( menu );
        karaMenu.addMenuListener( this );
        return true;
    }

	/* (non-Javadoc)
	 * @see de.karnik.karaim.MenuListener#aboutClicked()
	 */
	public void aboutClicked() {
		
		// create and show the about dialog
		StringBuffer title = new StringBuffer();
		
		title.append( getString( R.string.text_about ) ).append( ' ' );
		title.append( getString( R.string.app_name ) ).append( ' ' );
		title.append( getString( R.string.app_version ) );
		
		Dialog dd = new Dialog( this );
		
		dd.setTitle( title ); 
		dd.setContentView( R.layout.layout_about );
		dd.show(); 
	}

	/* (non-Javadoc)
	 * @see de.karnik.karaim.MenuListener#availableClicked()
	 */
	public void availableClicked() {
		KaraIM.status = BaseProtocol.STATUS_AVAILABLE;
		listHandler.post( changeStatus );
		
	}

	/* (non-Javadoc)
	 * @see de.karnik.karaim.MenuListener#awayClicked()
	 */
	public void awayClicked() {
		KaraIM.status = BaseProtocol.STATUS_AWAY;
		listHandler.post( changeStatus );
	}

	/* (non-Javadoc)
	 * @see de.karnik.karaim.MenuListener#dndClicked()
	 */
	public void dndClicked() {
		KaraIM.status = BaseProtocol.STATUS_DND;
		listHandler.post( changeStatus );
	}
	
	/* (non-Javadoc)
	 * @see de.karnik.karaim.MenuListener#invisibleClicked()
	 */
	public void invisibleClicked() {
		KaraIM.status = BaseProtocol.STATUS_INVISIBLE;
		listHandler.post( changeStatus );
		
	}

	/* (non-Javadoc)
	 * @see de.karnik.karaim.MenuListener#offlineClicked()
	 */
	public void offlineClicked() {
		KaraIM.status = BaseProtocol.STATUS_OFFLINE;
		listHandler.post( changeStatus );
	}

	/* (non-Javadoc)
	 * @see de.karnik.karaim.MenuListener#preferencesClicked()
	 */
	public void preferencesClicked() {
		Intent i = new Intent( this, KaraIMConfigWindow.class);
	    startSubActivity( i, 0 );
	}
	
	/* (non-Javadoc)
	 * @see de.karnik.karaim.MenuListener#exitClicked()
	 */
	public void exitClicked() {
		shutdown = true;
		KaraIM.status = BaseProtocol.STATUS_OFFLINE;
		listHandler.post( changeStatus );
		listHandler.post( stopKaraIM );
	}	
	
	/* (non-Javadoc)
	 * @see android.content.ServiceConnection#onServiceConnected(android.content.ComponentName, android.os.IBinder)
	 */
	public void onServiceConnected( ComponentName name, IBinder service ) {
		if( karaIMService == null )
			karaIMService = KaraIMServiceAIDL.Stub.asInterface( ( IBinder )service );
		
		LogFunctions.log( "KaraIm.onServiceConnected", "Service connected!" );
	}

	/* (non-Javadoc)
	 * @see android.content.ServiceConnection#onServiceDisconnected(android.content.ComponentName)
	 */
	public void onServiceDisconnected( ComponentName arg0 ) {
		karaIMService = null;
		LogFunctions.log( "KaraIm.onServiceDisconnected", "Service disconnected!" );
		
		if( shutdown )
			this.finish();
	}

	/* (non-Javadoc)
	 * @see android.widget.AdapterView$OnItemClickListener#onItemClick(android.widget.AdapterView, android.view.View, int, long)
	 */
	public void onItemClick( AdapterView arg0, View arg1, int arg2, long arg3 ) {
		Intent i = new Intent( this, KaraIMChatWindow.class);
		i.setLaunchFlags( arg2 );
	    startSubActivity( i, arg2 );
	}
	
	/**
	 * This inner class implements the base adapter for the contact list activity.
	 * 
	 * @author Markus Karnik - markus.karnik@my-design.org
	 * @version 1.0
	 * @since 1.0
	 */
	private class RosterAdapter extends BaseAdapter implements ImRosterListener {

	    /**
	     * The context object.
	     */
	    private Context context;
	    /**
	     * The ImRoster object.
	     */
	    private ImRoster roster;
	    
	    /**
	     * The contacts for the lust.
	     */
	    private ImContact[] contacts;
		
	    /**
	     * Constructs a RosterAdapter object with the given context.
	     * 
	     * @param context The invoking context.
	     */
	    public RosterAdapter( Context context ) {
	    	this.context = context;
	    	
	        roster = ImRoster.getInstance();
	        roster.addImRosterListener( this );
	        
	        contacts = roster.getContacts();
	    }
	    
	    /* (non-Javadoc)
	     * @see android.widget.BaseAdapter#areAllItemsSelectable()
	     */
	    public boolean areAllItemsSelectable() {
	    	return false;
	    }
	    
		/* (non-Javadoc)
		 * @see android.widget.Adapter#getCount()
		 */
		public int getCount() {
			return contacts.length;
		}

		/* (non-Javadoc)
		 * @see android.widget.Adapter#getItem(int)
		 */
		public Object getItem( int position ) {
			return position;
		}

		/* (non-Javadoc)
		 * @see android.widget.Adapter#getItemId(int)
		 */
		public long getItemId( int position ) {
			return position;
		}

		/* (non-Javadoc)
		 * @see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)
		 */
		public View getView( int position, View convertView, ViewGroup parent ) {
	        
			ContactView cv;
			
			LogFunctions.log( "KaraIm.getView", "getView" );
		
	        if (convertView == null) {
	            cv = new ContactView( context, contacts[ position ].getUserName(), 
	            					  contacts[ position ].getUserStatus(),
	            					  contacts[ position ].getNewIncomingMessageCount() );
	        } else {
	            cv = ( ContactView )convertView;
	            cv.setName( contacts[ position ].getUserName() );
	            cv.setStatus( contacts[ position ].getUserStatus() );
	            cv.setMessageCounterView(  contacts[ position ].getNewIncomingMessageCount() );
	        }
	        
	        return cv;
		}
		
		/**
		 * This inner class implements the contact view object for every contact in the list.
		 * 
		 * @author Markus Karnik - markus.karnik@my-design.org
		 * @version 1.0
		 * @since 1.0
		 */
	    private class ContactView extends LinearLayout {
	        
	    	/**
	    	 * The contact name view.
	    	 */
	    	private TextView nameView;
	    	/**
	    	 * The message counter view.
	    	 */
	    	private TextView messageCounterView;
	        /**
	         * The contact status image view.
	         */
	        private ImageView statusView;
	    	
	    	/**
	    	 * Constructs a ContactView object with the given context, name, status and counter.
	    	 * 
	    	 * @param context The invoking counter.
	    	 * @param name The initial contact name.
	    	 * @param status The initial contact status.
	    	 * @param counter The initial new message counter.
	    	 */
	    	public ContactView( Context context, String name, int status, int counter ) {
	            super( context );
	            
	            LogFunctions.log( "ContactView.ContactView", "ContactView" );
	            
	            setOrientation( HORIZONTAL );
	            setPadding( 10, 10, 10, 10 );
	            setPreferredHeight( 30 );
	            
	            statusView = new ImageView( context );
	            setStatus( status );
	            addView( statusView, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT ) );
	            
	            
	            nameView = new TextView( context );
	            nameView.setPadding( 10, 0, 0, 0);
	            nameView.setText( name );
	            nameView.setGravity( 0x11 );
	            addView( nameView, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT ) );
	            
	            messageCounterView = new TextView( context );
	            messageCounterView.setPadding( 10, 0, 0, 0);
	            setMessageCounterView( counter );
	            messageCounterView.setGravity( 0x11 );
	            addView( messageCounterView, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT ) );
	            
	        }

			/**
			 * Sets the contact name.
			 * 
			 * @param name The contact name to set.
			 */
			public void setName( String name) {
				nameView.setText( name );
			}
			
			/**
			 * Sets the message counter value. Will be displayed when counter > 0.
			 * 
			 * @param counter The message counter value.
			 */
			public void setMessageCounterView( int counter ) {
	            if( counter > 0 )
	            	messageCounterView.setText( "(" + counter + ")" );
	            else
	            	messageCounterView.setText( "" );				
			}

			/**
			 * Sets the status icon value.
			 * 
			 * @param status The status value for the icon.
			 */
			public void setStatus( int status ) {		
	            // set icon
	            if( status == BaseProtocol.STATUS_AVAILABLE ) {
	            	statusView.setImageResource( R.drawable.icon_available );
	            } else if ( status == BaseProtocol.STATUS_AWAY ) {
	            	statusView.setImageResource( R.drawable.icon_away );
	            } else if ( status == BaseProtocol.STATUS_DND ) {
	            	statusView.setImageResource( R.drawable.icon_dnd );
	            } else if ( status == BaseProtocol.STATUS_INVISIBLE ) {
	            	statusView.setImageResource( R.drawable.icon_invisible );
	            } else if ( status == BaseProtocol.STATUS_OFFLINE ) {
	            	statusView.setImageResource( R.drawable.icon_offline );
	            }
	            
			}
	    }

		/* (non-Javadoc)
		 * @see de.karnik.libkara.roster.ImRosterListener#rosterChanged()
		 */
		public void rosterChanged() {
	        contacts = roster.getContacts();
	        listHandler.post( updateContactList );
		}
	}
}